/*
 * Project Name spring-boot-tools
 * File Name TableUtil
 * Package Name com.huxiaosu.toole.autocreate.utils
 * Create Time 2024/7/9
 * Create by name：liujie
 */
package com.huxiaosu.tools.autocreate.utils;

import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.annotation.*;
import com.baomidou.mybatisplus.core.toolkit.StringPool;
import com.huxiaosu.tools.autocreate.annotation.ColumnExtend;
import com.huxiaosu.tools.autocreate.annotation.TableExtend;
import com.huxiaosu.tools.autocreate.common.Constants;
import org.apache.commons.lang3.StringUtils;

import java.lang.reflect.Field;
import java.sql.JDBCType;
import java.util.*;

/**
 * Description
 *
 * @author liujie
 * @date 2024/7/9 15:07
 */
public class TableUtil {
    /**
     * 从 spring.datasource.url 中获取数据库名字
     *
     * @param dbUrl jdbc:mysql://127.0.0.1:3306/asdc_boot?....
     * @return java.lang.String
     * @author liujie
     * @date 2024/2/27 14:50
     */
    public static String getDbName(final String dbUrl) {
        if (StringUtils.isBlank(dbUrl)) {
            throw new RuntimeException("dbUrl Cannot be empty");
        }
        String[] split = dbUrl.split("/");
        List<String> list = Arrays.asList(split);
        String s = list.get(3);
        String[] split1 = s.split("\\?");
        List<String> list1 = Arrays.asList(split1);
        return list1.get(0);
    }

    /**
     * 获取表名
     *
     * @param clas 类
     * @return java.lang.String
     * @author liujie
     * @date 2023/3/10 11:00
     */
    public static String getTableName(Class<?> clas) {
        TableName tableNamePlus = clas.getAnnotation(TableName.class);
        if (tableNamePlus == null) {
            throw new RuntimeException(clas+"没有配置 TableName  ");
        }
        if (StringUtils.isBlank(tableNamePlus.value())) {
            throw new RuntimeException(clas+"TableName配置错误");
        }
        return tableNamePlus.value();
    }

    /**
     * 获取表描述
     *
     * @param clas 类
     * @return java.lang.String
     * @author liujie
     * @date 2023/3/10 11:00
     */
    public static String getTableComment(Class<?> clas) {
        TableExtend tableExtend = clas.getAnnotation(TableExtend.class);
        if (tableExtend != null) {
            return tableExtend.comment();
        }
        return StringPool.EMPTY;
    }

    public static String getTableEngine() {
        return Constants.MYSQL_DEFAULT_ENGINE;
    }

    public static String getTableDefaultChat() {
        return Constants.MYSQL_DEFAULT_CHAT;
    }

    /**
     * 判断字段是否包含- Mybatis-plus的注解
     *
     * @param field 字段
     * @return boolean
     * @author liujie
     * @date 2023/3/10 11:13
     */
    public static boolean hasColumnAnnotation(Field field) {
        TableField tableField = field.getAnnotation(TableField.class);
        TableId tableId = field.getAnnotation(TableId.class);
        TableLogic tableLogic = field.getAnnotation(TableLogic.class);
        // 没有注解，或者exist=false 表示非数据库字段
        boolean tableFieldFlag = tableField == null || !tableField.exist();
        // 没有 tableId 注解
        boolean tableFieldIdFlag = tableId == null;
        // 没有 tableLogic 注解
        boolean tableFieldLogiFlag = tableLogic == null;
        return !tableFieldFlag || !tableFieldIdFlag || !tableFieldLogiFlag;
    }

    /**
     * 获取字段的长度
     *
     * @param field Field
     * @return int
     * @author liujie
     * @date 2023/6/26 15:16
     */
    public static int getColumnLength(Field field) {
        ColumnExtend annotation = field.getAnnotation(ColumnExtend.class);
        if (annotation != null) {
            return annotation.length();
        }
        return 0;
    }

    /**
     * 获取字段的类型
     * <li>判断 javaType 和 columnExtend 的 type 是否一致； 不一致按照 javaType 获取</li>
     * @param field Field
     * @return int
     * @author liujie
     * @date 2023/6/26 15:16
     */
    public static String getColumnType(Field field) {
        ColumnExtend annotation = field.getAnnotation(ColumnExtend.class);
        // 判断 javaType 和 columnExtend 的 type 是否一致； 不一致按照 javaType 获取
        String javaType = field.getGenericType().toString();
        JDBCType columnExtendType = JDBCType.VARCHAR;
        if (annotation != null) {
            columnExtendType = annotation.type();
        }
        JDBCType javaColumnType = JavaTypeToJdbcType.javaToJdbcTypeMap.get(javaType);
        if(columnExtendType == javaColumnType){
            return columnExtendType.getName();
        }
        return javaColumnType.getName();
    }

    /**
     * 获取字段的描述
     *
     * @param field Field
     * @return int
     * @author liujie
     * @date 2023/6/26 15:16
     */
    public static String getColumnComment(Field field) {
        ColumnExtend annotation = field.getAnnotation(ColumnExtend.class);
        if (annotation != null) {
            return annotation.comment();
        }
        return StringPool.EMPTY;
    }

    /**
     * 判断字段是否可以为空
     *
     * @param field Field
     * @return int
     * @author liujie
     * @date 2023/6/26 15:16
     */
    public static boolean getColumnNullable(Field field) {
        ColumnExtend annotation = field.getAnnotation(ColumnExtend.class);
        if (annotation != null) {
            return annotation.nullable();
        }
        return true;
    }

    public static Field[] getAllField(Class<?> clas) {
        Field[] fields = clas.getDeclaredFields();
        return recursionParents(clas, fields);
    }

    /**
     * 获取类上所有字段
     *
     * @param clas 指定类
     * @return java.util.List<com.huxiaosu.boot.starter.datasource.autocreate.util.FieldInfo>
     * @author liujie
     * @date 2024/3/22 23:09
     */
    public static List<FieldInfo> getAllFields(Class<?> clas) {
        List<FieldInfo> fieldList = new ArrayList<>();
        // 判断是否有父类，如果有拉取父类的field，这里只支持多层继承
        Field[] fields = TableUtil.getAllField(clas);

        for (Field field : fields) {
            // 判断方法中是否有指定注解类型的注解
            if (TableUtil.hasColumnAnnotation(field)) {
                // 当前字段有效 ; 追加为建表字段
                FieldInfo fieldInfo = new FieldInfo();
                fieldInfo.setColumn(TableUtil.getColumnName(field));
                fieldInfo.setJdbcType(TableUtil.getColumnType(field));
                fieldInfo.setComment(TableUtil.getColumnComment(field));
                fieldInfo.setNull(TableUtil.getColumnNullable(field));
                fieldInfo.setKey(TableUtil.isKey(field));
                if (fieldInfo.isKey()) {
                    fieldInfo.setAutoIncrement(TableUtil.isAutoIncrement(field));
                }
                // 判断
                int columnLength = TableUtil.getColumnLength(field);
                if (columnLength > 0 && fieldInfo.isNeedLength()) {
                    fieldInfo.setNeedLength(true);
                    fieldInfo.setLength(columnLength);
                }


                fieldList.add(fieldInfo);
            }
        }
        return fieldList;
    }

    /**
     * 递归扫描父类的fields
     *
     * @param clas   类
     * @param fields 属性
     */
    @SuppressWarnings("rawtypes")
    private static Field[] recursionParents(Class<?> clas, Field[] fields) {
        if (clas.getSuperclass() != null) {
            Class clsSup = clas.getSuperclass();
            List<Field> fieldList = new ArrayList<>(Arrays.asList(fields));
            // 获取当前class的所有fields的name列表
            List<String> fdNames = getFieldNames(fieldList);
            for (Field pfd : clsSup.getDeclaredFields()) {
                // 避免重载属性
                if (fdNames.contains(pfd.getName())) {
                    continue;
                }
                fieldList.add(pfd);
            }
            fields = new Field[fieldList.size()];
            int i = 0;
            for (Object field : fieldList.toArray()) {
                fields[i] = (Field) field;
                i++;
            }
            fields = recursionParents(clsSup, fields);
        }
        return fields;
    }

    private static List<String> getFieldNames(List<Field> fieldList) {
        List<String> fdNames = new ArrayList<>();
        for (Field fd : fieldList) {
            fdNames.add(fd.getName());
        }
        return fdNames;
    }

    /**
     * 获取列名
     *
     * @param field entity 对象中属性
     * @return java.lang.String
     * @author liujie
     * @date 2023/3/10 14:12
     */
    public static String getColumnName(Field field) {
        TableField tableField = field.getAnnotation(TableField.class);
        TableId tableId = field.getAnnotation(TableId.class);
        TableLogic tableLogic = field.getAnnotation(TableLogic.class);
        if (tableField != null && !StringUtils.isEmpty(tableField.value()) && tableField.exist()) {
            return tableField.value().toLowerCase().replace(Constants.SQL_ESCAPE_CHARACTER, "");
        }
        if (tableId != null && !StringUtils.isEmpty(tableId.value())) {
            return tableId.value().replace(Constants.SQL_ESCAPE_CHARACTER, "");
        }
        if (tableLogic != null) {
            String name = field.getName();
            return StrUtil.toUnderlineCase(name);
        }
        return "";
    }

    /**
     * 是不是主键
     *
     * @param field 对象字段
     * @return boolean
     * @author liujie
     * @date 2023/3/10 14:29
     */
    public static boolean isKey(Field field) {
        TableId tableId = field.getAnnotation(TableId.class);
        return tableId != null;
    }

    /**
     * 判断字段是不是自增
     *
     * @param field 对象字段
     * @return boolean
     * @author liujie
     * @date 2023/3/10 14:31
     */
    public static boolean isAutoIncrement(Field field) {
        TableId tableId = field.getAnnotation(TableId.class);
        ColumnExtend columnExtend = field.getAnnotation(ColumnExtend.class);
        String javaType = field.getGenericType().toString();
        JDBCType columnExtendJdbcType = columnExtend.type();
        JDBCType jdbcType = JavaTypeToJdbcType.javaToJdbcTypeMap.get(javaType);
        if(columnExtendJdbcType != jdbcType){
            throw new RuntimeException("类型配置错误；field type = "+javaType+" ,columnExtend jdbcType = "+columnExtendJdbcType.getName());
        }
        if(!javaType.contains("Integer") && !javaType.contains("int")){
            // 不是 int 不支持自增
            return false;
        }
        // 如果主键 扩展类型是 String 那么不允许自增
        if (tableId != null) {
            IdType type = tableId.type();
            return type == IdType.AUTO;
        }
        return false;
    }

    /**
     * 根据数据库中表的结构和model中表的结构对比找出新增的字段
     *
     * @param allFieldList model中的所有字段
     * @param columnNames  数据库中的结构
     * @return 新增的字段
     */
    public static List<FieldInfo> getAddFieldList(List<FieldInfo> allFieldList, List<String> columnNames) {
        List<String> lowerColumn = new ArrayList<>(columnNames.size());
        for (String columnName : columnNames) {
            lowerColumn.add(columnName.toLowerCase());
        }
        List<FieldInfo> addFieldList = new ArrayList<>();
        for (FieldInfo obj : allFieldList) {
            // 循环新的model中的字段，判断是否在数据库中已经存在
            if (!lowerColumn.contains(obj.getColumn().toLowerCase())) {
                // 不存在，表示要在数据库中增加该字段
                addFieldList.add(obj);
            }
        }
        return addFieldList;
    }

    /**
     * 根据数据库中表的结构和model中表的结构对比找出删除的字段
     *
     * @param allFieldList model中的所有字段
     * @param columnNames  数据库中的结构
     * @return 新增的字段
     */
    public static List<FieldInfo> getRemoveFieldList(List<FieldInfo> allFieldList, List<String> columnNames) {
        List<String> lowerColumn = new ArrayList<>(columnNames.size());
        for (String columnName : columnNames) {
            lowerColumn.add(columnName.toLowerCase());
        }
        Map<String, FieldInfo> fieldMap = getAllFieldMap(allFieldList);
        List<FieldInfo> removeFieldList = new ArrayList<>();
        for (String fieldNm : lowerColumn) {
            // 判断该字段在新的model结构中是否存在
            if (fieldMap.get(fieldNm) == null) {
                // 不存在，做删除处理
                FieldInfo removeItem = new FieldInfo();
                removeItem.setColumn(fieldNm);
                removeFieldList.add(removeItem);
            }
        }
        return removeFieldList;
    }

    /**
     * 判断字段类型有没有更新
     *
     * @param dbColumn 数据库中的所有字段
     * @param allField model中的所有字段
     * @return 更新的字段
     * @author liujie
     * @date 2023/3/10 15:38
     */
    public static List<FieldInfo> getModifyFieldList(List<FieldInfo> dbColumn, List<FieldInfo> allField) {
        Map<String, FieldInfo> fieldMap = getAllFieldMap(allField);
        List<FieldInfo> modifyFieldList = new ArrayList<>();
        dbColumn.forEach(item -> {
            // 数据库中有该字段时，验证是否有更新
            FieldInfo fieldInfo = fieldMap.get(item.getColumn().toLowerCase());
            if (fieldInfo != null) {
                // 验证下类型是不是一直的
                boolean checkType = item.getJdbcType().equalsIgnoreCase(fieldInfo.getJdbcType());
                String comment = StringUtils.isNotBlank(fieldInfo.getComment())?fieldInfo.getComment():StringPool.EMPTY;
                String dbComment = StringUtils.isNotBlank(item.getComment())?fieldInfo.getComment():StringPool.EMPTY;
                boolean checkComment = dbComment.equalsIgnoreCase(comment);
                boolean checkLength = item.getLength() == fieldInfo.getLength();
                boolean checkNull = item.isNull() == fieldInfo.isNull();
                if (!checkType || !checkLength || !checkComment || !checkNull) {
                    modifyFieldList.add(fieldInfo);
                }
            }

        });
        return modifyFieldList;
    }

    /**
     * 将allFieldList转换为Map结构
     *
     * @param allFieldList 所有字段
     * @return map
     */
    public static Map<String, FieldInfo> getAllFieldMap(List<FieldInfo> allFieldList) {
        // 将fieldList转成Map类型，字段名作为主键
        Map<String, FieldInfo> fieldMap = new HashMap<>(allFieldList.size());
        for (FieldInfo item : allFieldList) {
            fieldMap.put(item.getColumn().toLowerCase(), item);
        }
        return fieldMap;
    }


}